#pragma once

#include <stx/core/time.hpp>

inline namespace stx
{
template<IsRawExStdChar Char_>
class BasicJoinSeparator final : public std::basic_string_view<Char_>
{
public:
    constexpr BasicJoinSeparator() noexcept = default;

    constexpr BasicJoinSeparator(Char_ const* const sz,
                                 std::size_t const  size) noexcept
        : std::basic_string_view<Char_>(sz, size)
    {}
};

template<IsRawExStdChar Char_>
class BasicPrefix final : public std::basic_string_view<Char_>
{
public:
    constexpr BasicPrefix() noexcept = default;

    constexpr BasicPrefix(Char_ const* const sz,
                          std::size_t const  size) noexcept
        : std::basic_string_view<Char_>(sz, size)
    {}
};

template<IsRawExStdChar Char_>
class BasicPostfix final : public std::basic_string_view<Char_>
{
public:
    constexpr BasicPostfix() noexcept = default;

    constexpr BasicPostfix(Char_ const* const sz,
                           std::size_t const  size) noexcept
        : std::basic_string_view<Char_>(sz, size)
    {}
};

template<IsRawExStdChar ExStdChar_, std::size_t N>
struct BasicStringLiteral final
{
    using char_type      = ExStdChar_;
    using const_iterator = ExStdChar_ const*;

    char_type value[N];

    // NOLINTNEXTLINE
    constexpr BasicStringLiteral(ExStdChar_ const (&str_literal)[N]) noexcept
    {
        std::copy_n(str_literal, N, value);
    }

    [[nodiscard]] constexpr auto empty() const noexcept
    {
        return false;
    }

    [[nodiscard]] constexpr auto size() const noexcept
    {
        return N - 1;
    }

    [[nodiscard]] constexpr const_iterator data() const noexcept
    {
        return std::data(this->value);
    }

    [[nodiscard]] constexpr const_iterator begin() const noexcept
    {
        return std::begin(this->value);
    }

    [[nodiscard]] constexpr const_iterator end() const noexcept
    {
        return std::begin(this->value) + this->size();
    }

    [[nodiscard]] char_type const& operator[](IsIntegral auto const idx) const
    {
        return this->value[idx];
    }

    friend std::ostream& operator<<(std::ostream&             os,
                                    BasicStringLiteral const& sl) noexcept
    {
        return os << sl.value;
    }

    [[nodiscard]] constexpr
    operator std::basic_string_view<char_type>() const noexcept
    {
        return std::basic_string_view<char_type>(this->data(), this->size());
    }

    [[nodiscard]] constexpr std::basic_string_view<char_type>
    tosv() const noexcept
    {
        return *this;
    }
};

template<IsRawExStdChar ExStdChar_,
         IsNonVoid      Traits_ = std::char_traits<ExStdChar_>>
class BasicStringView final : public std::basic_string_view<ExStdChar_, Traits_>
{
    using Base_ = std::basic_string_view<ExStdChar_, Traits_>;

public:
    using typename Base_::size_type;

    constexpr BasicStringView() noexcept = default;

    // NOLINTNEXTLINE
    constexpr BasicStringView(ExStdChar_ const* const sz) noexcept : Base_(sz)
    {}

    constexpr BasicStringView(ExStdChar_ const* const sz,
                              size_type const         size) noexcept
        : Base_(sz, size)
    {}

    // NOLINTNEXTLINE
    constexpr BasicStringView(
        std::basic_string<ExStdChar_, Traits_> const& str) noexcept
        : Base_(str)
    {}
};

using JoinSeparator     = BasicJoinSeparator<char>;
using WideJoinSeparator = BasicJoinSeparator<wchar_t>;
using U8JoinSeparator   = BasicJoinSeparator<char8_t>;
using U16JoinSeparator  = BasicJoinSeparator<char16_t>;
using U32JoinSeparator  = BasicJoinSeparator<char32_t>;

using Prefix     = BasicPrefix<char>;
using WidePrefix = BasicPrefix<wchar_t>;
using U8Prefix   = BasicPrefix<char8_t>;
using U16Prefix  = BasicPrefix<char16_t>;
using U32Prefix  = BasicPrefix<char32_t>;

using Postfix     = BasicPostfix<char>;
using WidePostfix = BasicPostfix<wchar_t>;
using U8Postfix   = BasicPostfix<char8_t>;
using U16Postfix  = BasicPostfix<char16_t>;
using U32Postfix  = BasicPostfix<char32_t>;

using StringView     = BasicStringView<char>;
using WideStringView = BasicStringView<wchar_t>;
using U8StringView   = BasicStringView<char8_t>;
using U16StringView  = BasicStringView<char16_t>;
using U32StringView  = BasicStringView<char32_t>;

template<std::size_t N>
using StringLiteral = BasicStringLiteral<char, N>;
template<std::size_t N>
using WideStringLiteral = BasicStringLiteral<wchar_t, N>;
template<std::size_t N>
using U8StringLiteral = BasicStringLiteral<char8_t, N>;
template<std::size_t N>
using U16StringLiteral = BasicStringLiteral<char16_t, N>;
template<std::size_t N>
using U32StringLiteral = BasicStringLiteral<char32_t, N>;

[[nodiscard]] constexpr JoinSeparator
operator""_I(char const* const sz, std::size_t const size) noexcept
{
    return JoinSeparator(sz, size);
}

[[nodiscard]] constexpr WideJoinSeparator
operator""_I(wchar_t const* const sz, std::size_t const size) noexcept
{
    return WideJoinSeparator(sz, size);
}

[[nodiscard]] constexpr U8JoinSeparator
operator""_I(char8_t const* const sz, std::size_t const size) noexcept
{
    return U8JoinSeparator(sz, size);
}

[[nodiscard]] constexpr U16JoinSeparator
operator""_I(char16_t const* const sz, std::size_t const size) noexcept
{
    return U16JoinSeparator(sz, size);
}

[[nodiscard]] constexpr U32JoinSeparator
operator""_I(char32_t const* const sz, std::size_t const size) noexcept
{
    return U32JoinSeparator(sz, size);
}

[[nodiscard]] constexpr Prefix
operator""_prefix(char const* const sz, std::size_t const size) noexcept
{
    return Prefix(sz, size);
}

[[nodiscard]] constexpr WidePrefix
operator""_prefix(wchar_t const* const sz, std::size_t const size) noexcept
{
    return WidePrefix(sz, size);
}

[[nodiscard]] constexpr U8Prefix
operator""_prefix(char8_t const* const sz, std::size_t const size) noexcept
{
    return U8Prefix(sz, size);
}

[[nodiscard]] constexpr U16Prefix
operator""_prefix(char16_t const* const sz, std::size_t const size) noexcept
{
    return U16Prefix(sz, size);
}

[[nodiscard]] constexpr U32Prefix
operator""_prefix(char32_t const* const sz, std::size_t const size) noexcept
{
    return U32Prefix(sz, size);
}

[[nodiscard]] constexpr Postfix
operator""_postfix(char const* const sz, std::size_t const size) noexcept
{
    return Postfix(sz, size);
}

[[nodiscard]] constexpr WidePostfix
operator""_postfix(wchar_t const* const sz, std::size_t const size) noexcept
{
    return WidePostfix(sz, size);
}

[[nodiscard]] constexpr U8Postfix
operator""_postfix(char8_t const* const sz, std::size_t const size) noexcept
{
    return U8Postfix(sz, size);
}

[[nodiscard]] constexpr U16Postfix
operator""_postfix(char16_t const* const sz, std::size_t const size) noexcept
{
    return U16Postfix(sz, size);
}

[[nodiscard]] constexpr U32Postfix
operator""_postfix(char32_t const* const sz, std::size_t const size) noexcept
{
    return U32Postfix(sz, size);
}

[[nodiscard]] constexpr StringView
operator""_sv(char const* const sz, std::size_t const size) noexcept
{
    return StringView(sz, size);
}

[[nodiscard]] constexpr WideStringView
operator""_sv(wchar_t const* const sz, std::size_t const size) noexcept
{
    return WideStringView(sz, size);
}

[[nodiscard]] constexpr U8StringView
operator""_sv(char8_t const* const sz, std::size_t const size) noexcept
{
    return U8StringView(sz, size);
}

[[nodiscard]] constexpr U16StringView
operator""_sv(char16_t const* const sz, std::size_t const size) noexcept
{
    return U16StringView(sz, size);
}

[[nodiscard]] constexpr U32StringView
operator""_sv(char32_t const* const sz, std::size_t const size) noexcept
{
    return U32StringView(sz, size);
}

template<IsAnyType>
struct IsStringLiteral__ final : std::false_type
{};

template<std::size_t N>
struct IsStringLiteral__<BasicStringLiteral<char, N>> final : std::true_type
{};

template<typename T>
concept IsStringLiteral = IsStringLiteral__<T>::value;

template<IsAnyType>
struct IsWideStringLiteral__ final : std::false_type
{};

template<std::size_t N>
struct IsWideStringLiteral__<BasicStringLiteral<wchar_t, N>> final
    : std::true_type
{};

template<typename T>
concept IsWideStringLiteral = IsWideStringLiteral__<T>::value;

template<IsAnyType>
struct IsU8StringLiteral__ final : std::false_type
{};

template<std::size_t N>
struct IsU8StringLiteral__<BasicStringLiteral<char8_t, N>> final
    : std::true_type
{};

template<typename T>
concept IsU8StringLiteral = IsU8StringLiteral__<T>::value;

template<IsAnyType>
struct IsU16StringLiteral__ final : std::false_type
{};

template<std::size_t N>
struct IsU16StringLiteral__<BasicStringLiteral<char16_t, N>> final
    : std::true_type
{};

template<typename T>
concept IsU16StringLiteral = IsU16StringLiteral__<T>::value;

template<IsAnyType>
struct IsU32StringLiteral__ final : std::false_type
{};

template<std::size_t N>
struct IsU32StringLiteral__<BasicStringLiteral<char32_t, N>> final
    : std::true_type
{};

template<typename T>
concept IsU32StringLiteral = IsU32StringLiteral__<T>::value;

template<typename T>
concept IsExStdZstring =
    IsExStdClassicString<T> || IsExStdString<T> || IsIdentical<T, StringView> ||
    IsIdentical<T, WideStringView> || IsIdentical<T, U8StringView> ||
    IsIdentical<T, U16StringView> || IsIdentical<T, U32StringView> ||
    IsStringLiteral<T> || IsWideStringLiteral<T> || IsU8StringLiteral<T> ||
    IsU16StringLiteral<T> || IsU32StringLiteral<T>;

template<typename T>
concept IsStdZstring = IsStdClassicString<T> || IsStdString<T> ||
    IsIdentical<T, StringView> || IsIdentical<T, WideStringView> ||
    IsStringLiteral<T> || IsWideStringLiteral<T>;
} // namespace stx

namespace ranges
{
template<stx::IsRawExStdChar Char_>
constexpr auto enable_borrowed_range<stx::BasicStringView<Char_>> = true;

template<stx::IsRawExStdChar Char_>
constexpr auto enable_borrowed_range<stx::BasicPrefix<Char_>> = true;

template<stx::IsRawExStdChar Char_>
constexpr auto enable_borrowed_range<stx::BasicJoinSeparator<Char_>> = true;

template<stx::IsRawExStdChar Char_>
constexpr auto enable_borrowed_range<stx::BasicPostfix<Char_>> = true;

template<stx::IsRawExStdChar Char_, std::size_t N>
constexpr auto enable_borrowed_range<stx::BasicStringLiteral<Char_, N>> = true;
} // namespace ranges